﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Net.Sockets;
using RedCorona.Net;
using System.Net;

namespace HovisPresent.Net
{
	public delegate void ClientConnected( ClientConnection connection );
	public delegate void ClientClosed( ClientConnection connection );
	public delegate void ClientMessageRecieved( ClientConnection connection, string message );
	public delegate void ClientConnectFailed( ClientConnection connection, Exception e);

	public class ClientConnection : Connection
	{
		private  string _host;
		public string Host { get { return _host; } }
		
		public event ClientClosed OnClientClosed;
		public event ClientMessageRecieved OnClientMessageRecieved;
		public event ClientConnected OnClientConnected;
		public event ClientConnectFailed OnClientConnectFailed;

		protected ClientInfo client;
		protected Socket sock;

		protected bool asyncConnectCompleted = false;

		/// <summary>
		/// As the client connection can be closed during async connect, the object can still
		/// be about even though the main app has forgotton about it. set this to true to ignore
		/// the rest of the connect code and just shutdown neatly
		/// </summary>
		protected bool killed = false;

		/// <summary>
		/// Connects the specified host.
		/// </summary>
		/// <param name="host">The host.</param>
		/// <param name="port">The port.</param>
		public void Connect(string host, int port) {
			_host = host;
			CreateAsyncConnectSocket(host, port);
		}

		/// <summary>
		/// Creates the async connect socket.
		/// </summary>
		/// <param name="address">The address.</param>
		/// <param name="port">The port.</param>
		protected void CreateAsyncConnectSocket(string address, int port) {
			sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			asyncConnectCompleted = false;
			killed = false;
			sock.BeginConnect(address, port, new AsyncCallback(ConnectCallback), this);
		}

		protected void PrepareClientInfo() {
			client = new ClientInfo(sock, false);
			client.OnRead += new ConnectionRead(OnMessageRecieved);
			client.OnClose += new ConnectionClosed(OnClosed);
			client.Delimiter = "\r\n";
			client.BeginReceive();
		}

		/// <summary>
		/// Connects the callback.
		/// </summary>
		/// <param name="ar">The ar.</param>
		private static void ConnectCallback(IAsyncResult ar) {
			ClientConnection client = (ClientConnection)ar.AsyncState;
			client.asyncConnectCompleted = true;
			try {
				// Complete the connection.
				client.sock.EndConnect(ar);

				Debug.WriteLine("Connected to slave " + client.Host);

				if (client.killed)
					return;

				client.PrepareClientInfo();

				// Signal that the connection has been made.
				if (client.OnClientConnected != null)
					client.OnClientConnected(client);

			} catch (Exception e) {
				Debug.WriteLine("Error connecting to slave " + client.Host + " : " + e);

				if (client.killed)
					return;

				// Signal that the connection has not been made.
				if (client.OnClientConnectFailed!= null)
					client.OnClientConnectFailed(client,e);
			}
		}

		/// <summary>
		/// Closes this instance.
		/// </summary>
		public void CloseSocket() {
			killed = true;
			try {
				if (client != null && !client.Closed)
					client.Close();
			} catch {} 

			client = null;

			try {
				sock.Close();
			} catch { }
			
			sock = null;
		}

		/// <summary>
		/// Closes this instance and removes events
		/// </summary>
		public void Close() {
			OnClientClosed = null;
			OnClientConnected = null;
			OnClientMessageRecieved = null;
			OnClientConnectFailed = null;
			CloseSocket();
		}

		/// <summary>
		/// Gets a value indicating whether this instance is connected.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is connected; otherwise, <c>false</c>.
		/// </value>
		public bool IsConnected {
			get {
				return client != null && asyncConnectCompleted && !client.Closed;
			}
		}

		/// <summary>
		/// Called when [closed].
		/// </summary>
		/// <param name="ci">The ci.</param>
		private void OnClosed(ClientInfo ci) {
			Debug.WriteLine("Client closed");
			
			if (OnClientClosed!=null)
				OnClientClosed(this);
		}

		/// <summary>
		/// Called when [message recieved].
		/// </summary>
		/// <param name="ci">The ci.</param>
		/// <param name="text">The text.</param>
		private void OnMessageRecieved(ClientInfo ci, String text) {
			Debug.WriteLine("Received text message: " + text);
			
			if (OnClientMessageRecieved!=null)
				OnClientMessageRecieved(this, text);
		}

		/// <summary>
		/// Sends the message, adding crlf character
		/// </summary>
		/// <param name="message">The message.</param>
		protected internal void SendMessage(string message) {
			if (client == null || client.Closed)
				return;
			message += client.Delimiter;
			client.Send(message);
		}
	}
}
